﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using InChatter.Service.Data;
using System.Web.Script.Serialization;
using System.Diagnostics;

namespace InChatter.Service
{
    public class Chat : IChat
    {
        public static string MessagesPath = "LeaveMessages\\";
        public static Dictionary<string, IChatCallback> Callbacks = new Dictionary<string, IChatCallback>();
        public static Dictionary<string, string> AllClients = null;
        private string _currentClientId;
        public bool Login(string clientId)
        {
            if (Callbacks.Keys.Contains(clientId))
            {
                return false;
            }
            IChatCallback callback;
            //Broadcast the online message
            BroadcastUserState(clientId, true);
            callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
            Callbacks.Add(clientId, callback);
            _currentClientId = clientId;
            return true;
        }

        public void ReceiveLeaveMsg()
        {
            IChatCallback callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
            using (var db = NDatabase.OdbFactory.Open(string.Format("{0}{1}.db", MessagesPath, _currentClientId)))
            {
                List<InChatterMessage> list = db.AsQueryable<InChatterMessage>().ToList();
                foreach (InChatterMessage msg in list)
                {
                    callback.ReceiveMsg(msg);
                    db.Delete(msg);
                }
            }
            return;
        }

        public string GetOnlineClient()
        {
            IChatCallback callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
            //get all the online client except the client who requests
            var result = Callbacks.Where(p => p.Value != callback).Select(p => p.Key).ToList();
            return string.Join(",", result);
        }

        public void SynchronousAllClients(string allClients)
        {
            JavaScriptSerializer jss = new JavaScriptSerializer();
            try
            {
                AllClients = jss.Deserialize<Dictionary<string, string>>(allClients);
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("InChatter",ex.Message,EventLogEntryType.Error);
            }
        }

        public void SendMsg(Data.InChatterMessage message)
        {
            if (message == null)
            {
                return;
            }
            if (message.Type == "notice")
            {
                SendNotice(message);
            }
            else if (message.Type == "msg")
            {
                if (Callbacks.ContainsKey(message.ReceiverID))
                {
                    try
                    {
                        Callbacks[message.ReceiverID].ReceiveMsg(message);
                    }
                    catch
                    {
                        Store(message, message.ReceiverID);
                        Logout(message.ReceiverID);
                    }
                }
                else
                {
                    Store(message, message.ReceiverID);
                }
            }
        }



        public void Logout(string clientId)
        {
            try
            {
                lock (Callbacks)
                {
                    //remove the logoff client
                    Callbacks.Remove(clientId);
                }
                //Broadcast the logoff message
                BroadcastUserState(clientId, false);
            }
            catch
            {

            }
        }

        /// <summary>
        /// alert a client's logon or logoff event
        /// </summary>
        /// <param name="employeeId"></param>
        /// <param name="isLogin"></param>
        private void BroadcastUserState(string clientId, bool isLogon)
        {
            List<string> list = new List<string>();
            foreach (var item in Callbacks)
            {
                InChatterMessage txt = new InChatterMessage();
                txt.IsRead = false;
                txt.Content = clientId;
                txt.SenderID = "";
                txt.ReceiverID = item.Key;
                if (isLogon)
                {
                    txt.Type = "logon";
                }
                else
                {
                    txt.Type = "logoff";
                }
                txt.SendTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                try
                {
                    item.Value.ReceiveMsg(txt);
                }
                catch
                {
                    list.Add(item.Key);
                }
            }
            RemoveDisconnectCallBacks(list);
        }

        /// <summary>
        /// Send notice to clients except the sender
        /// </summary>
        /// <param name="msg"></param>
        private void SendNotice(InChatterMessage msg)
        {
            if (AllClients == null) return;
            List<string> list = new List<string>();
            foreach (var item in AllClients)
            {

                if (Callbacks.ContainsKey(item.Key) && !item.Key.Equals(_currentClientId, StringComparison.Ordinal))
                {
                    try
                    {
                        Callbacks[item.Key].ReceiveMsg(msg);
                    }
                    catch
                    {
                        Store(msg, item.Key);
                        list.Add(item.Key);
                    }
                }
                else
                {
                    Store(msg, item.Key);
                }

            }
            RemoveDisconnectCallBacks(list);
        }

        /// <summary>
        /// Remove the dead clients
        /// </summary>
        /// <param name="list"></param>
        private void RemoveDisconnectCallBacks(List<string> list)
        {
            if (list.Count > 0)
            {
                //remove the dead clients
                foreach (var item in list)
                {
                    Callbacks.Remove(item);
                }
                //tell the online client about the dead clients
                foreach (var item in list)
                {
                    BroadcastUserState(item, false);
                }
            }
        }

        private void Store(InChatterMessage message, string clientId)
        {
            using (var db = NDatabase.OdbFactory.Open(string.Format("{0}{1}.db", MessagesPath, clientId)))
            {
                db.Store<InChatterMessage>(message);
            }
        }
    }
}
